package com.yang.aspect;


import com.alibaba.fastjson2.JSON;
import com.yang.annotation.OptLogger;
import com.yang.domain.entity.OperationLog;
import com.yang.asynLog.AsyncFactory;
import com.yang.asynLog.AsyncManager;
import com.yang.mapper.OperationLogMapper;
import com.yang.utils.IpUtils;
import com.yang.utils.UserHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;


/**
 * <p>
 * AOP记录操作日志
 * </p>
 * @author yang
 * @date 2023/6/25
 */
@Aspect
@Component
public class OptLogAspect {

    /**
     * 请求开始时间
     */
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Autowired
    private OperationLogMapper operationLogMapper;

    /**
     * 设置操作日志切入点，记录操作日志，在注解的位置切入代码
     */
    @Pointcut("@annotation(com.yang.annotation.OptLogger)")
    public void optLogPointCut() {
    }

    /**
     * 前置通知
     */
    @Before("optLogPointCut()")
    public void doBefore() {
        // 记录请求开始时间
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 连接点正常返回通知，拦截用户操作日志，正常执行完成后执行， 如果连接点抛出异常，则不会执行
     * todo 周宇辉 3
     * @param joinPoint 切面方法的信息
     * @param result    返回结果
     */
    @AfterReturning(value = "optLogPointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Api api = (Api) signature.getDeclaringType().getAnnotation(Api.class);
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        OptLogger optLogger = method.getAnnotation(OptLogger.class);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        methodName = className + "." + methodName;
        String ip = IpUtils.getIpAddress(request);

        OperationLog operationLog = OperationLog
                .builder()
                .module(api.tags()[0])
                .type(optLogger.value())
                .uri(request.getRequestURI())
                .name(methodName)
                .description(apiOperation.value())
                .params(JSON.toJSONString(joinPoint.getArgs()))
                .method(Objects.requireNonNull(request).getMethod())
                .data(JSON.toJSONString(result))
                .userId(UserHolder.getUser().getId())
                .nickname(UserHolder.getUser().getNickName())
                .ipAddress(ip)
                .ipSource(IpUtils.getIpSource(ip))
                .times(System.currentTimeMillis() - startTime.get())
                .createTime(LocalDateTime.now()).build();


        // AsyncManager.getInstance().execute(AsyncFactory.recordOperation(operationLog));

        operationLogMapper.insert(operationLog);
    }
}